package com.shopping_assistant.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.util.Log;

import com.shopping_assistant.view.overlays.SAItemizedOverlay;

public class ShoppingInterpreter implements LocationListener {

	private static final int Max_Buffer_Size = 2;
	private static final double Exiting_Range = 5.0;
	private static final double Entering_Range = 5.0;
	private static final double Accuracy_Thresshold = 20.0;

	private ShopController shopController;
	private SAItemizedOverlay saItemizedOverlay;

	private Shop lastVisitedShop;
	private Shop movingToShop;
	private List<Double> lastDistancesToClosest;
	private List<Double> lastDistancesToVisiting;
	private Map<String, Location> lastLocations;
	private Person activePerson; 

	public ShoppingInterpreter(SAItemizedOverlay saItemizedOverlay) {
		shopController = ShopController.getInstance();
		this.saItemizedOverlay = saItemizedOverlay;

		lastLocations = new HashMap<String,Location>();
		lastDistancesToClosest = new ArrayList<Double>(Max_Buffer_Size);
		lastDistancesToVisiting = new ArrayList<Double>(Max_Buffer_Size);
		activePerson = PersonController.getInstance().getActiveUser();
		lastVisitedShop = null;
		movingToShop = null;
	}

	public void onLocationChanged(Location location) {
		interpretData(location);
	}

	public void onProviderDisabled(String arg0) {
		throw new UnsupportedOperationException();
	}

	public void onProviderEnabled(String arg0) {
		throw new UnsupportedOperationException();
	}

	public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
		throw new UnsupportedOperationException();
	}

	private synchronized void interpretData(Location currentLocation) {
		
		Log.d("CurrentLocation", String.format("Provider: %s, Location: %s", currentLocation.getProvider(), currentLocation.toString()));
		
		lastLocations.put(currentLocation.getProvider(), currentLocation);
		
		if(currentLocation != null && currentLocation.getAccuracy() <= Accuracy_Thresshold) {
			Shop closestShop = shopController.findClosestShop(currentLocation);
		
			double currentDistance = closestShop.distanceTo(currentLocation);
			addToLastDistances(Double.valueOf(currentDistance), lastDistancesToClosest);
			
			if(lastVisitedShop != null) {
				currentDistance = lastVisitedShop.distanceTo(currentLocation);
				addToLastDistances(currentDistance, lastDistancesToVisiting);
			}
			
			if(lastVisitedShop != closestShop && movingToShop != closestShop) {
				movingToShop = closestShop;
			}
			
			isExitingShop();
			isEnteringShop(closestShop);
		}
	}

	/**
	 * LastVisited Shop is only unpoped if we are certain that the person is
	 * working away from the shop.
	 * 
	 * Since positioning is more unstable indoors, the last five positions are
	 * saved to make sure that the person is actually leaving the shop and not
	 * the sensor jittering.
	 * 
	 */
	private void isExitingShop() {

		if(lastVisitedShop != null) {
			if (lastDistancesToVisiting.size() == Max_Buffer_Size && lastDistancesToVisiting.get(0) >= Exiting_Range) {
				
				for (int i = 1; i < lastDistancesToVisiting.size(); i++) {
					if (lastDistancesToVisiting.get(i - 1) < lastDistancesToVisiting.get(i))
						return;
				}
				
				Log.d("CurrentLocation", "Exiting shop!");
				saItemizedOverlay.unpopItem(lastVisitedShop);
				lastVisitedShop = null;
				lastDistancesToVisiting.clear();
			}
		}
	}

	private void isEnteringShop(Shop closestShop) {
		
		if (lastVisitedShop != closestShop && lastDistancesToClosest.size() == Max_Buffer_Size && lastDistancesToClosest.get(0) <= Entering_Range) {

			for (int i = 1; i < lastDistancesToClosest.size(); i++) {
				if (lastDistancesToClosest.get(i - 1) > lastDistancesToClosest.get(i))
					return;
			}
			
			Log.d("CurrentLocation", "Entering shop!");
			
			saItemizedOverlay.tapItem(closestShop);
			lastVisitedShop = closestShop;
			
			activePerson.shopVisited(closestShop);
		}
	}
	
	private void addToLastDistances(Double newDistance, List<Double> list) {
		
		if(list.size() == Max_Buffer_Size)
			list.remove(Max_Buffer_Size - 1);
		
		list.add(0, newDistance);
	}
}
